
import 'package:flutter/material.dart';
import 'dart:async';

class ElapsedTime {
  final int minutes;
  final int seconds;
  final int millseconds;

  ElapsedTime({
    this.minutes,
    this.seconds,
    this.millseconds,
  });
}

class TimerComponent {
  final List<ValueChanged<ElapsedTime>> timerListeners = <ValueChanged<ElapsedTime>>[];
  final TextStyle textStyle = const TextStyle(fontSize: 50.0, fontFamily: "Bebas Neue");
  final Stopwatch stopwatch = new Stopwatch();
  final int timerMillisecondsRefreshRate = 30;
}

class TimerPlane extends StatefulWidget {

  TimerPlane({Key key}):super(key:key);

  @override
  _TimerPlaneState createState() => _TimerPlaneState();
}

class _TimerPlaneState extends State<TimerPlane> {
  final TimerComponent timerComponent = new TimerComponent();

  @override
  Widget build(BuildContext context) {
    return new Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        new Expanded(
          flex: 1,
          child: new TimerText(timerComponent: timerComponent),
        ),
        new Expanded(
          flex: 2,
          child: new Padding(
            padding: const EdgeInsets.symmetric(horizontal: 1.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                buildFloatingButton("Start", startButtonPressed),
                buildFloatingButton("Stop", stopButtonPressed),
                buildFloatingButton("Round", roundButtonPressed),
              ],
            ),
          )
        )
      ],
    );
  }

  Widget buildFloatingButton(String text, VoidCallback callback) {
    TextStyle textStyle = const TextStyle(fontSize: 16.0, color: Colors.black);
    return new FloatingActionButton(
      child: new Text(text, style: textStyle),
      onPressed: callback,
    );
  }

  void startButtonPressed() {
    setState(() {
      if ( !timerComponent.stopwatch.isRunning ) {
        timerComponent.stopwatch.start();
      }
    });
  }

  void stopButtonPressed() {
    setState(() {
      if ( timerComponent.stopwatch.isRunning ) {
        timerComponent.stopwatch.stop();
      }
    });
  }

  void roundButtonPressed() {
    setState(() {
      if ( timerComponent.stopwatch.isRunning ) {
        timerComponent.stopwatch.reset();
      }
    });
  }
}

class TimerText extends StatefulWidget {
  final TimerComponent timerComponent;

  TimerText({this.timerComponent});

  @override
  _TimerTextState createState() => _TimerTextState(timerComponent: timerComponent);
}

class _TimerTextState extends State<TimerText> {
  final TimerComponent timerComponent;

  Timer timer;
  int milliseconds;

  _TimerTextState({@required this.timerComponent});

  @override
  void initState() {
    timer = new Timer.periodic(new Duration(milliseconds: timerComponent.timerMillisecondsRefreshRate), callback);
    super.initState();
  }

  @override
  void dispose() {
    timer?.cancel();
    timer = null;
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        new RepaintBoundary(
          child: new SizedBox(
            height: 20.0,
            child: new MinutesAndSeconds(timerComponent: timerComponent),
          ),
        ),
        new RepaintBoundary(
          child: new SizedBox(
            height: 20.0,
            child: new MilliSeconds(timerComponent: timerComponent),
          ),
        )
      ],
    );
  }


  void callback(Timer timer) {
    if ( milliseconds != timerComponent.stopwatch.elapsedMilliseconds ) {
      milliseconds = timerComponent.stopwatch.elapsedMilliseconds;
      final int millseconds = (milliseconds).truncate();
      final int seconds = (millseconds / 1000).truncate();
      final int minutes = (seconds / 60).truncate();

      final ElapsedTime elapsedTime = new ElapsedTime(
        millseconds: millseconds,
        seconds: seconds,
        minutes: minutes,
      );

      for ( final listener in timerComponent.timerListeners ) {
        listener(elapsedTime);
      }
    }
  }
}


class MinutesAndSeconds extends StatefulWidget {
  TimerComponent timerComponent;

  MinutesAndSeconds({this.timerComponent});

  @override
  _MinutesAndSecondsState createState() => _MinutesAndSecondsState(timerComponent: timerComponent);
}

class _MinutesAndSecondsState extends State<MinutesAndSeconds> {
  final TimerComponent timerComponent;

  _MinutesAndSecondsState({@required this.timerComponent});

  int minutes = 0;
  int seconds = 0;

  @override
  void initState() {
    timerComponent.timerListeners.add(onTick);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    String minuteStr = (minutes % 60).toString().padLeft(2, '0');
    String secondStr = (seconds % 60).toString().padLeft(2, '0');
    return new Text('$minuteStr:$secondStr:', style: timerComponent.textStyle);
  }

  void onTick(ElapsedTime elapsed) {
    if ( elapsed.minutes != minutes || elapsed.seconds != seconds) {
      setState(() {
        minutes = elapsed.minutes;
        seconds = elapsed.seconds;
      });
    }
  }
}

class MilliSeconds extends StatefulWidget {
  final TimerComponent timerComponent;
  MilliSeconds({this.timerComponent});

  @override
  _MilliSecondsState createState() => _MilliSecondsState(timerComponent: timerComponent);
}

class _MilliSecondsState extends State<MilliSeconds> {
  final TimerComponent timerComponent;
  _MilliSecondsState({@required this.timerComponent});

  int milliseconds = 0;

  @override
  void initState() {
    timerComponent.timerListeners.add(onTick);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new Text((milliseconds % 10000).toString().padLeft(4, '0'), style: timerComponent.textStyle);
  }

  void onTick(ElapsedTime elapsed) {
    if ( elapsed.millseconds != milliseconds ) {
      setState(() {
        milliseconds = elapsed.millseconds;
      });
    }
  }
}